
class ContentWithFields < ContainerThing

  Pattern::Property.enable(self,
    :field,
    :on_define => lambda { |*a|
      a[0].define_field *a
    },
    :getter => false,
    :setter => false
  )

  @@__fields ||= {}

  def self.define_field klass, name, definition
    name = name.to_s
    field_instance = definition[0]
    field_instance.name = name
    if field_instance.respond_to? 'on_plug'
      field_instance.on_plug klass, name, definition.tail
    end
    (@@__fields[klass] ||= {})[name] = field_instance#.freeze
  end

  def on_plug klass, name, definition
    #puts "\n\nON PLUG #{self}"

    (@@__fields[self.class] || []).each { |name, field|
      #p [name, field]
      #field.on_plug(self.class, name, [field])
    }
  end


  def creator e
    auto_migrate
    super
  end

  def recieve e
    auto_migrate
    super
  end


  def auto_migrate
    self.class.fields.each { |k, field_definition|
      #p [k, children.map(&:name)]
      if !children.select { |ch|
            (ch.respond_to? :name) &&
            (ch.name == k.to_s)
        }.first
        begin
          add self.class.field_at(k).dup
        rescue Exception => e
          raise "Cannot migrate #{k}: "+e.message
        end
      end
    }
    children.each { |ch| ch.auto_migrate if ch.respond_to? :auto_migrate }
    self
  end

  def self.field_at name
    @@__fields[self][name.to_s]
  end

  def self.fields
    Pattern::Property.properties(self, "field")
  end






  class Test < FwTest

    class MyTestClassForPlace < ContentWithFields
      # Class.new doesn't work well with DataMapper, oh well..
    end

    def test_get_fields
      place = PlaceField.new
      MyTestClassForPlace.field(:place, place)
      eq MyTestClassForPlace.fields, {:place => [place]}
    end


    class MyColorField < Thing
      attr_accessor :called_with, :name

      def on_plug *a
        @called_with = a
      end
    end

    def test_field_takes_an_instance
      field = MyColorField.new()
      baloon = Class.new(ContentWithFields) {
        field(:color, field)
      }
      eq baloon.field_at(:color), field
    end

    def test_when_field_is_defined_its_on_define_method_gets_called
      color = MyColorField.new
      baloon = Class.new(ContentWithFields) {
        field(:color, color)
      }
      eq baloon.field_at(:color).called_with, [baloon, 'color', []]
    end

    def test_auto_migrate_adds_missing_fields
      color_field = MyColorField.new
      baloon = Class.new(ContentWithFields) {
        field :color, color_field
      }

      my_baloon = baloon.new
      my_baloon.remove_children
      my_baloon.auto_migrate

      # unit tests cannot compare frozen objects,
      # so we dup the frozen one
      eq my_baloon.children, [color_field.dup]
    end


    def test_auto_migrate_adds_only_missing_fields
      color_field = MyColorField.new
      baloon = Class.new(ContentWithFields) {
        field :color, color_field
      }

      my_baloon = baloon.new
      my_baloon.remove_children
      my_baloon.auto_migrate
      my_baloon.auto_migrate
      my_baloon.auto_migrate

      # unit tests cannot compare frozen objects,
      # so we dup the frozen one
      eq my_baloon.children, [color_field.dup]
    end
  end
end


